package afcepf.atod18.frameworkMVC2.servlet;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import net.htmlparser.jericho.*;

public class FormGenerator {
    private static final Logger logger = Logger.getLogger(FormGenerator.class);

    private Map<String, FormGenerator_Form> forms = new HashMap<String, FormGenerator_Form>();

    /**
     * Scan all directories of web application fro HTML files parse each files
     * to recognize Form and Fields generate associated java Form class
     * 
     * @return List of HTML files found
     */
    public Map<String, FormGenerator_Form> searchHMTL() {

        FilesSearch fileSearch = new FilesSearch();

        fileSearch.searchDirectory(new File(Thread.currentThread().getContextClassLoader().getResource("/../../").getPath()),
                ".html");

        int count = fileSearch.getResult().size();
        if (count == 0) {
            logger.info("FormGenerator:FindForms: No result found!");
        } else {
            logger.info("FormGenerator:FindForms: Found " + count + " file(s)");
            for (String matched : fileSearch.getResult()) {
                logger.info("FormGenerator:FindForms: Found : " + matched);
                try {
                    parseHTMLFile(matched);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return forms;
    }

    /**
     * parse a HTML File and extract form and related fields
     * 
     * @param sourceUrlString
     * @throws MalformedURLException
     * @throws IOException
     */
    private void parseHTMLFile(String sourceUrlString) throws MalformedURLException, IOException {
        sourceUrlString = "file:" + sourceUrlString;
        logger.info("ParseHTMLFile: parsing " + sourceUrlString);
        // Don't forget to set setFormFieldNameCaseInsensitive to false
        // otherwise all fields name are in lowercase
        net.htmlparser.jericho.Config.CurrentCompatibilityMode.setFormFieldNameCaseInsensitive(false);
        Source source = new Source(new URL(sourceUrlString));
        List<Element> elementList = source.getAllElements(HTMLElementName.FORM);
        if (elementList.size() > 0) {
            logger.info("-------FORM Detected-----------------------------------------------------------");
            String formName = elementList.get(0).getAttributeValue("name");
            FormGenerator_Form formInfo = new FormGenerator_Form();
            formInfo.setFormName(formName);
            formInfo.setUrl(sourceUrlString);
            logger.info("ParserHMTLFile: Found Form of name " + formName);
            FormFields formFields = source.getFormFields();
            for (FormField formField : formFields) {
                logger.info("ParseHTMLFile: FormField name " + formField.getName());
                formInfo.getFields().add(formField);
                // logger.info("ParseHTMLFile: FormField debug info\n"+formField.getDebugInfo());
            }
            logger.info("-------------------------------------------------------------------------------");
            formInfo.setSourceCode(generateClass(formName, formFields));
            // store information about this form (form name in lowercase) in
            // forms
            forms.put(formName.toLowerCase(), formInfo);
        }
    }

    public String FillFields(FormGenerator_Form formInfo, Map<String,String> fieldsValues, String servletContext) {
        logger.info("FillFields: filling fields of form " + formInfo.getUrl());
        // Don't forget to set setFormFieldNameCaseInsensitive to false
        // otherwise all fields name are in lowercase
        net.htmlparser.jericho.Config.CurrentCompatibilityMode.setFormFieldNameCaseInsensitive(false);
        Source source;
        try {
            source = new Source(new URL(formInfo.getUrl()));

            // retrive name of form from HTML file
            List<Element> elementList = source.getAllElements(HTMLElementName.FORM);
            if (elementList.size() > 0) {
                String formName = elementList.get(0).getAttributeValue("name");
                logger.info("FillFields: Form of name " + formName);
                // retrieve fields from HTML File
                FormFields formFields = source.getFormFields();
                // Clear values of fields
                formFields.clearValues();
                // add good values for fields
                for (String fieldname:fieldsValues.keySet()) {
                    formFields.addValue(fieldname, fieldsValues.get(fieldname));
                }
                // generate a new HTML file, name ErrorFieldsForm.html
                OutputDocument outputDocument=new OutputDocument(source);
                outputDocument.replace(formFields); // adds all segments necessary to effect changes

                String fname = formInfo.getUrl();
                // remove URL file: sequence
                fname = fname.replace("file:", "");
                fname = fname.substring(0, fname.lastIndexOf("\\") );
                logger.info("FormGenerator:FillFields: path of new HTML file: "+fname);
                File f = new File(fname);
                fname = f.getPath()+"\\ErrorFieldsForm.html";
                Writer out=new FileWriter(fname);
                outputDocument.writeTo(out);
                out.close();
                
                // return name of page relative to application context
                servletContext = servletContext.replace("/", "\\");
                int pos = fname.indexOf(servletContext);
                fname = fname.substring( pos + servletContext.length(), fname.length() );
                fname = fname.replace("\\", "/");
                logger.info("FormGenerator:FillFields: return to file "+fname);
                return fname;
            }
        } catch (IOException e) {
            logger.error("FillFields: cannot fill fields of form " + formInfo.getUrl());
            e.printStackTrace();
        }
        return null;
    }

    /**
     * generateClass method
     * 
     * @param aForm
     *            the name of the ActionForm class
     * @param fields
     *            a list of fields extract from HTML file
     * @return String contains the generated java source code for class
     */
    public String generateClass(String aForm, FormFields fields) {
        StringWriter outSourceCode = new StringWriter();
        PrintWriter out = new PrintWriter(outSourceCode);
        out.println();
        out.println("//-----------------------------------------------------");
        out.println("// FrameworkMVC2 generated form class -> " + aForm);
        out.println("//-----------------------------------------------------");
        out.println();
        out.println("package form;");
        out.println();
        out.println("import afcepf.atod18.frameworkMVC2.servlet.ActionForm;");
        out.println();
        out.println(String.format("public class %s implements ActionForm {", aForm));
        // generate private fields
        for (FormField ff : fields) {
            out.println(String.format("   private String %s;", ff.getName()));
        }
        out.println();
        // generate getter and setter for each field
        for (FormField ff : fields) {
            String setter = ff.getName();
            String getter;
            setter = setter.substring(0, 1).toUpperCase() + setter.substring(1, setter.length());
            getter = "get" + setter;
            setter = "set" + setter;
            out.println(String.format("   public String %s() {", getter));
            out.println(String.format("      return %s;", ff.getName()));
            out.println("   }");
            out.println();
            out.println(String.format("   public void %s(String %s) {", setter, ff.getName()));
            out.println(String.format("      this.%s = %s;", ff.getName(), ff.getName()));
            out.println("   }");
            out.println();
        }
        out.println("   @Override");
        out.println("   public boolean validateForm() {");
        out.println("      return false;");
        out.println("   }");
        out.println();
        out.println("}");
        out.println("//-------end of class ------------------------------------------");
        out.println();
        out.close();

        logger.info("Generated source code is printed next lines :\n" + outSourceCode);
        return outSourceCode.toString();
    }

    /**
     * @return the forms
     */
    public Map<String, FormGenerator_Form> getForms() {
        return forms;
    }
}
